package com.minelx.serverandclient.common;

import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class EnsuredString {

	private String source;

	private EnsuredString(String source) {
		this.source = source;
	}

	@Override
	public String toString() {
		return source;
	}

	public byte[] getBytes() {
		return source.getBytes();
	}

	public EnsuredString range(int from, int to) {
		if (from >= source.length()) {
			return saveAs("");
		}
		if (from + to > source.length()) {
			return saveAs(source.substring(from));
		}
		return saveAs(source.substring(from, to));
	}

	public EnsuredString cap(String cap) {
		return saveAs(cap + source + cap);
	}

	public EnsuredString trimCap() {
		if (source.length() == 1) {
			return this;
		}
		if (surroundedBy(source.charAt(0))
				|| surroundedBy('(', ')')
				|| surroundedBy('[', ']')
				|| surroundedBy('{', '}')) {
			source = source.substring(1, last(1));
		}
		return this;
	}

	public EnsuredString cap(String left, String right) {
		return saveAs(left + source + right);
	}

	private boolean surroundedBy(char wrapper) {
		return surroundedBy(wrapper, wrapper);
	}

	private boolean surroundedBy(char left, char right) {
		return charIs(0, left) && charIs(last(1), right);
	}

	private int last(int count) {
		return source.length() - count;
	}

	private boolean charIs(int at, char expected) {
		return source.charAt(at) == expected;
	}

	public EnsuredString duplicate(String target, int times) {
		return saveAs(source + Stream.generate(() -> target)
				.limit(times)
				.collect(Collectors.joining()));
	}

	private EnsuredString saveAs(String source) {
		this.source = source;
		return this;
	}

	public static EnsuredString of(String source) {
		return new EnsuredString(source);
	}

	public static EnsuredString empty() {
		return of("");
	}

	public static EnsuredString duplicated(String target, int times) {
		return empty().duplicate(target, times);
	}

	public static EnsuredString iterate(String seed, UnaryOperator<String> iterator, int count) {
		return of(Stream.iterate(seed, iterator)
						.limit(count)
						.reduce((summary, addend) -> summary + addend)
						.orElseThrow());
	}
}
